use Test::More tests => 118;
#use Test::More qw(no_plan);

BEGIN {
use_ok( 'Firepear::Carrot::Lexer' );
}

# test object creation and handling with no input files
my $t = Firepear::Carrot::Lexer->new();
is($t->fetch,  undef, 'Still no input');
is($t->status,   901,      'Shouldnt exist ');

# test with bad input files
my @list = qw(foo/nofile);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is($t->fetch,  undef, 'Still no input');
is($t->status,   101, 'File doesn\'t exist');

# test with unreadable input file
@list = qw(./t/corpus/token.X);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is($t->fetch,  undef, 'Still no input');
is($t->status,   102, 'File exists but can\'t be opened');

# test with irregular file
@list = qw(./t/corpus/);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is($t->fetch,  undef, 'Still no input');
is($t->status,   103, 'File is not a regular file');

# test with good file
@list = qw(./t/corpus/token.1);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is($t->fetch,     '[[x', 'w00t, it works');
is($t->status,        0, 'File exists but can\'t be opened');
is($t->fetch,       ' ', 'w00t, it works');
is($t->fetch,  "a;'foo", 'w00t, it works');
is($t->fetch,       ' ', 'w00t, it works');
is($t->fetch,  "bar']]", 'w00t, it works');
is($t->fetch,      "\n", 'w00t, it works');
for (1..9) { $t->fetch }
is($t->fetch,   ']]', 'nearly there');
is($t->fetch,   "\n", 'last token of file');
is($t->fetch,  undef, 'nothing left');
is($t->status,    99, 'run complete');

# test with multiple good files
@list = qw(./t/corpus/token.1 ./t/corpus/token.2);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is($t->{f}{list}[0]{name}, './t/corpus/token.1',
   'first file, should be "token.1"');
is($t->fetch,     '[[x', 'beginning of file 1');
for (1..15) { $t->fetch }
is($t->fetch, "\n",   'last token of file 1');
is($t->fetch, "\n", 'synthetic token generated by EOF');
is($t->fetch, '[[z',  'beginning of file 2');

# test fileinfo
@list = qw(./t/corpus/token.1 ./t/corpus/token.4);
$t = Firepear::Carrot::Lexer->new( files => \@list );
for (1..62) { $t->fetch }
is($t->fetch, 'hymenaeos.', 'hymenaeos.');
my $fi = $t->fileinfo;
is(ref $fi,                       'HASH', 'should be a hashref');
is($fi->{fnum},                        2, 'second file');
is($fi->{name},     './t/corpus/token.4', 'second file name');
is($fi->{pos},                       165, 'file position');
is($fi->{lnum},                        3, 'third line');
is_deeply($fi->{line},  ['nostra,',' ','per',' ','inceptos',' ','hymenaeos.'],
          'line up to this point');
is($fi->{token},            'hymenaeos.', 'last-parsed token');

# test related fetches
my $line = $t->line;
is_deeply($line,  ['nostra,',' ','per',' ','inceptos',' ','hymenaeos.'],
          'line up to this point');
is ($t->prev, ' ', 'previous token');
is ($t->current, 'hymenaeos.', 'current token');


# test fpush
@list = qw(./t/corpus/token.4);
$t = Firepear::Carrot::Lexer->new( files => \@list );
is ($t->{f}{list}[0]{name}, './t/corpus/token.4', 'first and only file');
for (1..61) { $t->fetch }
is ($t->{t}{tok},  'Aliquam', 'where we stop...');
$t->fpush('./t/corpus/token.2');
is ($t->{f}{list}[0]{name}, './t/corpus/token.2', 'pushed file is first');
is ($t->{f}{list}[1]{name}, './t/corpus/token.4', 'parent file moves up one');
is ($t->fetch, '[[z', 'pulling from included file now');
for (1..10) { $t->fetch }
$fi = $t->fileinfo;
is($fi->{par}, './t/corpus/token.4', 'parent filename');
is($fi->{pline},                  4, 'parent line count');
is ($t->fetch,                 "\n", 'pulling from included file');
is ($t->fetch,                 "\n", 'synthetic (EOF)');
is ($t->fetch,                  ' ', 'pulling from parent file again');
is ($t->fetch,            'cursus,', 'pulling from parent file');

# test scalar-as-file
@list = ( { name   => 'fake1',
	    scalar => 1,
	    text   => "mary had a\nlittle lamb" } );
$t = Firepear::Carrot::Lexer->new( files => \@list );
is ($t->fetch, 'mary', 'beginning of synthetic file');
for (1..7) { $t->fetch }
is ($t->current, ' ');
is ($t->fetch, 'lamb', 'physical end of synthetic file');
is ($t->fetch, undef, 'undef');

# test mixed-mode new call
@list = ('./t/corpus/token.1',
	 { name   => 'fake1',
	   scalar => 1,
	   text   => "mary had a\nlittle lamb" },
	 { name   => './t/corpus/token.4',
	   scalar => 0 } );
$t = Firepear::Carrot::Lexer->new( files => \@list );
is ($t->fetch,     '[[x', 'beginning of file 1');
for (1..15) { $t->fetch }
is ($t->fetch, "\n",   'end of file 1');
is ($t->fetch, "\n", 'synthetic token (EOF)');
is ($t->fetch, 'mary', 'beginning of synthetic file');
$fi = $t->fileinfo;
is ($fi->{name}, 'fake1', 'synthetic file name');
for (1..5) { $t->fetch }
is ($t->{t}{tok}, "\n", 'next');
for (1..3) { $t->fetch }
is ($t->{t}{tok}, 'lamb',  'end of synthetic file');
is ($t->fetch,    "\n",  'synthetic token (synthetic EOF of synthetic file)');
is ($t->prev, "\n", 'newline, not lamb due to eof');
is ($t->fetch,    'Lorem', 'beginning of file 3');

# test scalar fpush
@list = qw(./t/corpus/token.4);
$t = Firepear::Carrot::Lexer->new( files => \@list );
for (1..61) { $t->fetch }
is ($t->{t}{tok},  'Aliquam', 'where we stop...');
$t->fpush( { name => 'foo', scalar => 1, text => 'bar baz quux' } );
is ($t->fetch, 'bar', 'pulling from included file now');
$t->fpush( { name => './t/corpus/token.2', scalar => 0 } );
is ($t->fetch, '[[z', 'pulling from second included file');
for (1..13) { $t->fetch }
is ($t->fetch, 'baz', 'pulling from first included file (plus one)');
for (1..4) { $t->fetch }
is ($t->fetch, 'cursus,', 'back in original file (plus one)');

# test bad scalar
@list = ( { name => 'fake2', scalar => 1 } );
$t = Firepear::Carrot::Lexer->new( files => \@list );
is ($t->fetch, undef, 'no token');
is ($t->status, 104,   'because we didnt pass any text with the scalar file');


# dupe checking
@list = ('./t/corpus/token.1', './t/corpus/token.1');
$t = Firepear::Carrot::Lexer->new( files => \@list );
is (defined $t->{f}{list}[0]{md5},     1, 'file 1 md5 exists');
is (defined $t->{f}{list}[1]{md5},    '', 'file 2 md5 undefined');
for (1..16) { $t->fetch }
is ($t->status,  0, 'ok');
$t->fetch;
is ($t->status, 105, 'dupe file detected');
@list = ('./t/corpus/token.1',
	 { name   => 'fake1', 
	   scalar => 1, 
	   text   => "[[x a;'foo bar']]\n\n[[y b;baz\n    c;quux\n           ]]\n" } );
$t = Firepear::Carrot::Lexer->new( files => \@list );
is (defined $t->{f}{list}[0]{md5},     1, 'file 1 md5 exists');
is (defined $t->{f}{list}[1]{md5},    '', 'file 2 md5 undefined');
for (1..16) { $t->fetch }
is ($t->status,  0, 'ok');
$t->fetch;
is ($t->status, 105, 'dupe file detected');


# unless (defined $readok) from lookahead
@list = qw(./t/corpus/token.2 ./t/corpus/token.1);
$l = Firepear::Carrot::Lexer->new( files => \@list );
for (1..12) { $l->fetch }
close $l->{f}{handle};
$l->lookahead;
is ($l->status, 100, 'unknown read error');
is ($l->{l}{type}, 999, 'sentinel value');

# settoken
@list = qw(./t/corpus/token.2 ./t/corpus/token.1);
$l = Firepear::Carrot::Lexer->new( files => \@list );
for (1..3) { $l->fetch }
is ($l->current, 'd;ramen]]', 'expected state');
is ($l->prev, ' ', 'expected prev state');
is (scalar @{$l->{t}{next}}, 0, 'expected next state');

is ($l->settoken, undef, 'settoken with no token');
is ($l->current, 'd;ramen]]', 'expected state');
is ($l->prev, ' ', 'expected prev state');
is (scalar @{$l->{t}{next}}, 0, 'expected next state');

is ($l->settoken(cur => 'foobar'), 'foobar', 'settoken with token');
is ($l->current, 'foobar', 'expected state 2');
is ($l->prev, ' ', 'expected prev state 2');
is (scalar @{$l->{t}{next}}, 0, 'expected next state 2');

is ($l->settoken(cur => 'barfoo', prev =>'zaq'), 'barfoo',
    'settoken with token, prev');
is ($l->current, 'barfoo', 'expected state 3');
is ($l->prev, 'zaq', 'expected prev state 3');
is (scalar @{$l->{t}{next}}, 0, 'expected next state 3');

is ($l->settoken(cur => 'quux', next =>'zaq'), 'quux',
    'settoken with token, next');
is ($l->current, 'quux', 'expected state 3.5');
is ($l->prev, 'zaq', 'expected prev state 3.5');
is ($l->{t}{next}[0], 'zaq', 'expected next state 3.5');
is ($l->status, 0, 'next, in media res');

is ($l->settoken(cur => 'b', prev => 'a', next => 'c'),
    'b', 'settoken with token, prev, next');
is ($l->current, 'b', 'expected state 4');
is ($l->prev, 'a', 'expected prev state 4');
is ($l->{t}{next}[1], 'c', 'expected next state 4');
is ($l->fetch, 'c', 'fetch pulled from next, not file');
is ($l->prev, 'b','prev was updated appropriately');
is (scalar @{$l->{t}{next}}, 1, 'expected next state 5');
is ($l->status, 0, 'next, in media res 2');

is ($l->fetch, "zaq", 'fetch pulled from next');
is ($l->fetch, "\n", 'fetch pulled from file');
is ($l->prev, 'zaq','prev was updated appropriately');
is (scalar @{$l->{t}{next}}, 0, 'expected next state 6');
is ($l->status, 0, 'next, in media res 3');

until ($l->status) {
    $l->fetch;
}
is ($l->status, 99, 'EOD');
$l->settoken(cur => 'blah', next => 'foo1');
is ($l->status, 1, 'EOD');
$l->settoken(cur => 'blah', next => 'foo2');
$l->settoken(cur => 'blah', next => 'foo3');
is ($l->fetch, 'foo3', 'fetch with status 1 via next');
is ($l->fetch, 'foo2', 'fetch with status 1 via next');
is ($l->fetch, 'foo1', 'fetch with status 1 via next');
is ($l->fetch, undef, 'end of next stack');
is ($l->status, 99, 'EOD, again');
